<?php
// +----------------------------------------------------------------------
// | UCToo [ Universal Convergence Technology ]
// +----------------------------------------------------------------------
// | Copyright (c) 2014-2017 http://uctoo.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: Patrick <contact@uctoo.com>
// +----------------------------------------------------------------------

namespace app\mpopen\controller;
use think\Controller;
use app\common\controller\Backend;
use EasyWeChat\Factory;
use EasyWeChat\OpenPlatform\Server\Guard;
use EasyWeChat\OpenPlatform\Auth\VerifyTicket;
use com\wxopen\wxBizMsgCrypt;
use com\TPWechatOpen;
use com\TPWechat;
use com\wxopen\Encryptor;
use Monolog\Logger;
use Monolog\Handler\StreamHandler;
use Monolog\Handler\FirePHPHandler;
use uctoo\MyCustomCache;
use app\common\model\User;
use app\admin\model\Admin;
use app\admin\model\AdminLog;

/*
 * 微信开放平台
 * */
class Mpopen extends Backend{

    protected $config;    //初始化easywechat sdk的参数
    protected $mpopen;    //数据库保存的开放平台数据
    protected $logger;
    protected $openPlatform;      //
    protected $weObj;
    protected $noNeedLogin = ['PreAuthorization','get_ticket','wxlogin_callback'];
    protected $noNeedRight = ['rec_page'];

    public function _initialize()
    {
        parent::_initialize();

//        $model = model('mpopen');
//        $this->mpopen = $model->where('status', 1)->find();
//        trace($this->mpopen,'Mpopen_$cfg:');
//        $this->config = [
//            'app_id'   => $this->mpopen->appid,
//            'secret'   => $this->mpopen->appsecret,
//            'token'    => $this->mpopen->component_access_token,
//            'aes_key'  => $this->mpopen->encodingAesKey,
//            /**
//             * 日志配置
//             *
//             * level: 日志级别, 可选为：
//             *         debug/info/notice/warning/error/critical/alert/emergency
//             * permission：日志文件权限(可选)，默认为null（若为null值,monolog会取0644）
//             * file：日志文件位置(绝对路径!!!)，要求可写权限
//             */
//            'log'     => [
//                'level'      => 'debug',
//                'permission' => 0777,
//                'file'       => ROOT_PATH . '/runtime/log/easywechat.log',
//            ],
//            /**
//             * OAuth 配置
//             *
//             * scopes：公众平台（snsapi_userinfo / snsapi_base），开放平台：snsapi_login
//             * callback：OAuth授权完成后的回调页地址
//             */
//            'oauth'   => [
//                'scopes'   => ['snsapi_login'],
//                'callback' => 'https://www.uctoo.com/mpopen/mpopen/get_ticket',
//            ],
//            /**
//             * Guzzle 全局设置
//             *
//             * 更多请参考： http://docs.guzzlephp.org/en/latest/request-options.html
//             */
//            'guzzle'  => [
//                'timeout' => 3.0, // 超时时间（秒）
//                //'verify' => false, // 关掉 SSL 认证（强烈不建议！！！）
//            ],
//        ];
//
//        trace($this->config,'Mpopen_$option:');
//        $this->openPlatform = Factory::openPlatform($this->config);
//        //$this->openPlatform['cache'] = new MyCustomCache();
//        $this->openPlatform['verify_ticket']->setTicket($this->mpopen->component_verify_ticket);
//        // 创建Logger实例
//        $this->logger = new Logger('my_logger');
//// 添加handler
//        $this->logger->pushHandler(new StreamHandler(ROOT_PATH . '/runtime/log/applog.log', Logger::DEBUG));
//        $this->logger->pushHandler(new FirePHPHandler());
//        $options['component_appid'] = $this->mpopen['appid'];    //初始化options信息
//        $options['component_appsecret'] = $this->mpopen['appsecret'];
//        $options['component_access_token'] = $this->mpopen['component_access_token'];
//        $options['encodingaeskey'] = $this->mpopen['encodingAesKey'];
//        $options['debug'] = config('app_debug');            //调试状态跟随系统调试状态
//        if($options['debug']){
//            $options['logcallback'] = 'trace';              //微信类调试信息用trace方法记录到TP日志文件中
//        }
//        $this->weObj = new TPWechatOpen($options);
    }

    /*
     * 接收component_verify_ticket
     *
     * */
    public function PreAuthorization(){
        /*$pre_auth_code = $this->openPlatform->createPreAuthorizationCode();
        $this->logger->addInfo($pre_auth_code['pre_auth_code']);
        $url = $this->openPlatform->getPreAuthorizationUrl('https://www.uctoo.com/mpopen/mpopen/rec_page',$pre_auth_code['pre_auth_code']); // 传入回调URI即可
        $this->logger->addInfo('PreAuthorization $url');
        $this->logger->addInfo($url);
            $options['component_appid'] = $this->mpopen['appid'];    //初始化options信息
            $options['component_appsecret'] = $this->mpopen['appsecret'];
            $options['component_access_token'] = $this->mpopen['component_access_token'];
            $options['encodingaeskey'] = $this->mpopen['encodingAesKey'];
            $options['debug'] = config('app_debug');            //调试状态跟随系统调试状态
            if($options['debug']){
                $options['logcallback'] = 'trace';              //微信类调试信息用trace方法记录到TP日志文件中
            }
            $weObj = new TPWechatOpen($options);*/
            $pre_auth_code = $this->weObj->getPreAuthCode();
            $url = $this->weObj->getPreAuthorizationUrl('https://www.uctoo.com/mpopen/mpopen/rec_page',$pre_auth_code); // 传入回调URI即可
            return $url;
    }

    /*
     * 接收component_verify_ticket
     *
     * */
    public function get_ticket(){
        $timeStamp = input('get.timestamp');
        $nonce = strval(input('get.nonce'));
        $msg_sign = strval(input('get.msg_signature'));
        $text = request()->getInput();
        $auth_code=input('get.auth_code');
        $res = $this::decrypt($timeStamp,$nonce,$msg_sign,$text);
        if($res){
            switch($res['InfoType']){
                case 'component_verify_ticket';      // 处理component_verify_ticket事件,初始录入mpopen数据缺少正确的component_access_token，不验证signature进行获取。
                    $model = new \app\common\model\Mpopen();
                    $model->save([
                        'component_verify_ticket'  => $res['ComponentVerifyTicket']
                    ],['appid'=>$this->mpopen['appid']]);          //接收到component_verify_ticket就保存到数据库
                    $this->mpopen['component_verify_ticket'] = $res['ComponentVerifyTicket'];  //数据库和查询值一致
                    $res = $this->receive_component_token($res['ComponentVerifyTicket']);
                    $res?printf('success'):null;//回复微信平台
                    break;
            }
        }

        $server = $this->openPlatform->server;

        //$server->getHandlers();
        // 处理component_verify_ticket事件
        //          $server->push(function ($message) {
    //              trace($message,'get_ticket_$message:');

        //          }, Guard::EVENT_COMPONENT_VERIFY_TICKET);
// 处理授权成功事件
               $server->push(function ($message) {
            // ...
                   trace($message,'get_ticket_AUTHORIZED:');
                   $this->logger->addInfo('My logger is EVENT_AUTHORIZED:');
                   $this->logger->addInfo(json_encode( $message));
                   $model = model('shop_cat');
                   $model->where('status', 1)->find();
             }, Guard::EVENT_AUTHORIZED);
// 处理授权更新事件
              $server->push(function ($message) {
            // ...
                  trace($message,'get_ticket_UPDATE_AUTHORIZED:');
                  $this->logger->addInfo('My logger is EVENT_UPDATE_AUTHORIZED:');
                  $this->logger->addInfo(json_encode( $message));
                  $model = model('shop_order');
                  $model->where('status', 1)->find();
             }, Guard::EVENT_UPDATE_AUTHORIZED);

// 处理授权取消事件
              $server->push(function ($message) {
            // ...
                  trace($message,'get_ticket_UNAUTHORIZED:');
                  $this->logger->addInfo('My logger is EVENT_UNAUTHORIZED:');
                  $this->logger->addInfo(json_encode( $message));
                  $model = model('shop_product');
                  $model->where('status', 1)->find();
             }, Guard::EVENT_UNAUTHORIZED);
              return $server->serve();
    }

    /*
     * 用户授权回调，获取公众号授权信息；
 * */
    public function rec_page($authorization_code=''){
        $auth_code=input('get.auth_code');
        $data = $this->weObj->getAuthorizationInfo($auth_code);
        $data['authorization_info']['access_token_overtime']=time()+$data['authorization_info']['expires_in'];
        //获取公众号信息
        $use_data = $this->weObj->getAuthorizerInfo($data['authorization_info']['authorizer_appid']);

        //授权成功，保存公众号信息
        $user_model = model('wechat_applet');
        $where['appid']=$data['authorization_info']['authorizer_appid'];
        $user = $user_model->where($where)->find();
        if($user){   //已授权过，更新授权信息
            $db_data['admin_id'] = $this->auth->id;
            $db_data['name'] = $use_data['authorizer_info']['nick_name'];
            $db_data['appletid'] = md5($data['authorization_info']['authorizer_appid']);
            $db_data['principal'] = $use_data['authorizer_info']['principal_name'];
            $db_data['original'] = $use_data['authorizer_info']['user_name'];
            $db_data['headface_image'] = $use_data['authorizer_info']['head_img'];
            $db_data['qrcode_image'] = $use_data['authorizer_info']['qrcode_url'];
            $db_data['service_type_info'] = json_encode($use_data['authorizer_info']['service_type_info']);
            $db_data['verify_type_info'] = json_encode($use_data['authorizer_info']['verify_type_info']);
            $db_data['business_info'] = json_encode($use_data['authorizer_info']['business_info']);
            $db_data['func_info'] = json_encode($use_data['authorization_info']['func_info']);
            $db_data['authorizer_access_token'] = $data['authorization_info']['authorizer_access_token'];
            $db_data['access_token_overtime'] = $data['authorization_info']['access_token_overtime'];
            $db_data['authorizer_refresh_token'] = $data['authorization_info']['authorizer_refresh_token'];
            if (array_key_exists('MiniProgramInfo', $use_data['authorizer_info'])) {   //是否是小程序授权
                $db_data['miniprograminfo'] = json_encode($use_data['authorizer_info']['MiniProgramInfo']);
                $db_data['signature'] = $use_data['authorizer_info']['signature'];
                $db_data['typedata'] = 'miniapp';
            }else{
                $db_data['wechat'] = $use_data['authorizer_info']['alias'];
                if($db_data['service_type_info'] == 0 || $db_data['service_type_info'] == 1){
                    $db_data['typedata'] = 'sub_account';
                }else{
                    $db_data['typedata'] = 'serv_account';
                }
            }
            $user_model->save($db_data,$where);
        }else{       //未授权过，新增授权公众号
            $db_data['appid'] = $use_data['authorization_info']['authorizer_appid'];
            $db_data['appletid'] = md5($data['authorization_info']['authorizer_appid']);
            $db_data['admin_id'] = $this->auth->id;
            $db_data['name'] = $use_data['authorizer_info']['nick_name'];
            $db_data['principal'] = $use_data['authorizer_info']['principal_name'];
            $db_data['original'] = $use_data['authorizer_info']['user_name'];
            $db_data['headface_image'] = $use_data['authorizer_info']['head_img'];
            $db_data['qrcode_image'] = $use_data['authorizer_info']['qrcode_url'];
            $db_data['service_type_info'] = json_encode($use_data['authorizer_info']['service_type_info']);
            $db_data['verify_type_info'] = json_encode($use_data['authorizer_info']['verify_type_info']);
            $db_data['business_info'] = json_encode($use_data['authorizer_info']['business_info']);
            $db_data['func_info'] = json_encode($use_data['authorization_info']['func_info']);
            $db_data['authorizer_access_token'] = $data['authorization_info']['authorizer_access_token'];
            $db_data['access_token_overtime'] = $data['authorization_info']['access_token_overtime'];
            $db_data['authorizer_refresh_token'] = $data['authorization_info']['authorizer_refresh_token'];
            if (array_key_exists('MiniProgramInfo', $use_data['authorizer_info'])) {   //是否是小程序授权
                $db_data['miniprograminfo'] = json_encode($use_data['authorizer_info']['MiniProgramInfo']);
                $db_data['signature'] = $use_data['authorizer_info']['signature'];
                $db_data['typedata'] = 'miniapp';
            }else{
                $db_data['wechat'] = $use_data['authorizer_info']['alias'];
                if($db_data['service_type_info'] == 0 || $db_data['service_type_info'] == 1){
                    $db_data['typedata'] = 'sub_account';
                }else{
                    $db_data['typedata'] = 'serv_account';
                }
            }
            $user_model->save($db_data);
        }

        $this->redirect('admin/wechat/applet');
    }


/*
 * 解密
* */
    public function decrypt($timeStamp,$nonce,$msg_sign,$text){
        $encodingAesKey = $this->mpopen['encodingAesKey'];
        $token = $this->mpopen['component_access_token'];
        $appId = $this->mpopen['appid'];
        $pc = new wxBizMsgCrypt($token, $encodingAesKey, $appId);
        $msg = '';
        $errCode = $pc->decryptMsg($msg_sign, $timeStamp, $nonce, $text, $msg, false);
        if ($errCode == 0) {
            return (array)simplexml_load_string($msg, 'SimpleXMLElement', LIBXML_NOCDATA) ;
        } else {
            return $errCode ;
        }
    }

    /*
     * 获取平台access_token，保存到开放平台表
* */
    public function receive_component_token($ComponentVerifyTicket=null){
        if(!$this->mpopen||time() >= $this->mpopen['token_overtime']){      //ComponentAccessToken过期，重新获取并保存到数据库
            $json = $this->weObj->getComponentAccessToken($ComponentVerifyTicket);
            $model = new \app\common\model\Mpopen();
            $token_overtime = time()+$json['expires_in']-700;
            $res = $model->save([
                'component_access_token' => $json['component_access_token'],
                'token_overtime' => $token_overtime
            ],['appid'=>$this->mpopen['appid']]);
            $this->mpopen['component_access_token'] = $json['component_access_token'];  //数据库和查询值一致
            $this->mpopen['token_overtime'] = $token_overtime;  //数据库和查询值一致
            if($res){
                return true;
            }else{
                return false;
            }
        }
        return true;
    }


    /**
     * wxlogin_callback 微信登录回调
     * @author:patrick contact@uctoo.com
     *
     */
    public function wxlogin_callback()
    {
        $orgin_state = session('wxlogin_state');
        trace($orgin_state,'$orgin_state:');  //TODO:验证state是否一致
        $state = input('state');
        trace($orgin_state,'wxlogin_callback$state:');
        //TODO:个别情况有bug，先不做验证17.05.31
        //if($orgin_state != $state){
        //    $this->error('state参数错误');
        //}
        $code = input('code');
        if(!$code){  //若用户禁止授权，则跳转回登录页面
            $this->redirect('admin/index/login');
        }
        $appletid = input('appletid');
        trace($appletid,'wxlogin_callback$mp_id:');
        $appinfo = model('wechat_applet')->where('appletid',$appletid)->find();
        trace($appinfo,'wxlogin_callback$appinfo:');

        $options['token'] = $appinfo['token'];
        $options['appid'] = $appinfo['appid'];    //初始化options信息
        $options['appsecret'] = $appinfo['appsecret'];
        $options['encodingaeskey'] = $appinfo['aeskey'];
        $options['debug'] = config('app_debug');            //调试状态跟随系统调试状态
        if($options['debug']){
            $options['logcallback'] = 'trace';              //微信类调试信息用trace方法记录到TP日志文件中
        }
        $weObj = new TPWechat($options);

        trace($weObj,'wxlogin_callback$weObj:');
        $result = $weObj->getOauthAccessToken();
        trace($result,'wxlogin_callback$result:');

        $user = User::get(['openid' => $result['openid']]);
        trace($user,'wxlogin_callback$user:');

        if($user){     //已存在用户，更新信息
            $user->access_token = $result['access_token'];
            $user->access_token_overtime = time()+$result['expires_in']-60;
            $user->refresh_token = $result['refresh_token'];
            $user->save();
            if($user->uid){      //存在会员帐号，登录会员
                $member = Admin::get($user->uid);

                //登录帐号
                AdminLog::setTitle(__('Login'));
                $result = $this->auth->wxlogin($member->username, 0);
                if ($result === true)
                {
                    $url = url('admin/index/index');
                    $this->success(__('Login successful'), $url, ['url' => $url, 'id' => $this->auth->id, 'username' => $member->username, 'avatar' => $this->auth->avatar]);
                }
                else
                {
                    $url = url('admin/index/login');
                    $this->error(__('Username or password is incorrect'), $url, ['token' => $this->request->token()]);
                }
            }
            $url = url('admin/index/login');
            $this->error(__('No account, Please register first'), $url, ['token' => $this->request->token()]);

        }else{         //新帐号，跳转到注册页
            $info = $weObj->getOauthUserinfo($result['access_token'],$result['openid']);
            $info['mp_id'] = $appinfo['id'];
            $info['access_token'] = $result['access_token'];
            $info['access_token_overtime'] = time()+$result['expires_in']-60;
            $info['refresh_token'] = $result['refresh_token'];
            trace($info,'wxlogin_callback$info:');
            session('user_info', $info);
            $this->redirect('admin/index/register');
        }
    }

}